package me.arvin.qa.jmeter.plugins.dependscontroller;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

import org.apache.jmeter.control.GenericController;
import org.apache.jmeter.control.LoopController;
import org.apache.jmeter.control.TransactionController;
import org.apache.jmeter.control.TransactionSampler;
import org.apache.jmeter.engine.event.LoopIterationEvent;
import org.apache.jmeter.engine.event.LoopIterationListener;
import org.apache.jmeter.engine.util.NoThreadClone;
import org.apache.jmeter.gui.tree.JMeterTreeNode;
import org.apache.jmeter.samplers.SampleEvent;
import org.apache.jmeter.samplers.SampleListener;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.samplers.Sampler;
import org.apache.jmeter.testelement.AbstractTestElement;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.testelement.ThreadListener;
import org.apache.jmeter.testelement.property.JMeterProperty;
import org.apache.jmeter.testelement.property.NullProperty;
import org.apache.jmeter.threads.JMeterContextService;

/**
 * @Function 依赖管理监听器 </br>
 *           线程启动时，给当前线程最后添加依赖管理监听器 </br>
 *           每个取样器执行时，检查当前是否有被依赖的组件 </br>
 *           当有新的迭代进入时，应从依赖控制器加入新的依赖控制器实例（命名规则？） 当
 *
 * @author wenke.dong
 *
 */
public class DependsManageThreadListener extends AbstractTestElement implements ThreadListener, NoThreadClone {
	private static final long serialVersionUID = 302154692767227915L;

	/**
	 * 全局的 只有一个 用来存依赖关系
	 */
	private static final DependsRelationMap drManager = new DependsRelationMap();

	static final DependsManageThreadListener instance = new DependsManageThreadListener();

	static final DependsManageSampleListener SampleListener = instance.new DependsManageSampleListener();
	static final DependsManageControllerListener ControllerListener = instance.new DependsManageControllerListener();

	/**
	 * 自己的 用来存结果（全局的副本）
	 */
	private static final ThreadLocal<DependsRelationMap> drMap = new ThreadLocal<>();

	private DependsManageThreadListener() {}

	public static boolean getDependsResolveResult(String id) {
		DependsRelationMap dependsOnMap = drMap.get();
		Map<String, SampleResult> map = dependsOnMap.getDependsOnMap().getOrDefault(id, new HashMap<>());
		if (map != null) {
			for (SampleResult res : map.values()) {
				if (res == null || !res.isSuccessful()) {
					return false;
				}
			}
		}
		return true;
	}

	static void addNodeDependsRelation(JMeterTreeNode node, DependsRelationProperty dc) {
		DependsManageTreeLisener.getInstance().addNodeDependsRelation(node, dc);
	}

	/**
	 * 配置阶段
	 *
	 * @param dc
	 */
	static void setDependsRelation(DependsRelationProperty dc) {
		/*
		 * 移除旧的依赖关系
		 */
		drManager.removeDependsRelation(dc.getId());
		/*
		 * 设置新的依赖关系
		 */
		dc.getDepends().forEach(id -> drManager.addDependsReleation(dc.getId(), id));
	}

	/**
	 * 全局依赖关系使用时 仅运行时拷贝
	 *
	 * @return
	 */
	private static DependsRelationMap cloneDependsRelationMap() {
		DependsRelationMap dependsRelationMap = new DependsRelationMap();

		drManager.getDependsOnMap().forEach(
				(id, v) -> v.keySet().forEach(beDependsId -> dependsRelationMap.addDependsReleation(id, beDependsId)));

		return dependsRelationMap;
	}

	@Override
	public void threadStarted() {
		if (drMap.get() == null) {
			drMap.set(cloneDependsRelationMap());
		}
	}

	@Override
	public void threadFinished() {}

	private void setExecuteResult(DependsRelationProperty drProp, SampleResult res) {
		if (drProp != null) {
			String dependsId = drProp.getId();
			DependsRelationMap dependsRelationMap = drMap.get();
			dependsRelationMap.getBeDependsOnMap().getOrDefault(dependsId, new HashSet<String>(0)).forEach(v -> {
				dependsRelationMap.getDependsOnMap().get(v).put(dependsId, res);
			});
		}
	}

	private class DependsManageSampleListener extends AbstractTestElement implements SampleListener, NoThreadClone {
		private static final long serialVersionUID = 700556136210031990L;

		@Override
		public void sampleOccurred(SampleEvent e) {// 事务控制器和子取样器没有
			if (!TransactionController.isFromTransactionController(e.getResult())) {
				Sampler currentSampler = JMeterContextService.getContext().getCurrentSampler();
				JMeterProperty drProp = currentSampler.getProperty(DependsController.DependsRelationList);
				if (!(drProp instanceof NullProperty)) {
					instance.setExecuteResult((DependsRelationProperty) drProp, e.getResult());
				}
			}
		}

		@Override
		public void sampleStarted(SampleEvent e) {

		}

		@Override
		public void sampleStopped(SampleEvent e) {

		}
	}

	private class DependsManageControllerListener extends AbstractTestElement
	implements LoopIterationListener, NoThreadClone {

		private static final long serialVersionUID = 700556136210031990L;

		@Override
		public void iterationStart(LoopIterationEvent e) {
			TestElement source = e.getSource();

			if (source instanceof LoopController) {
				resetLoop((LoopController) source);
			} else if (source instanceof TransactionController
					&& ((TransactionController) source).isGenerateParentSample()) {
				JMeterProperty drProp = source.getProperty(DependsController.DependsRelationList);
				if (!(drProp instanceof NullProperty)) {
					try {
						Field field = TransactionController.class.getDeclaredField("transactionSampler");
						field.setAccessible(true);
						TransactionSampler ts = (TransactionSampler) field.get(source);
						SampleResult transactionResult = ts.getTransactionResult();
						instance.setExecuteResult((DependsRelationProperty) drProp, transactionResult);

					} catch (NoSuchFieldException | SecurityException | IllegalArgumentException
							| IllegalAccessException e1) {
						e1.printStackTrace();
					}
				}
			}

		}

		@SuppressWarnings("unchecked")
		private void resetLoop(LoopController controller) {
			try {
				Method method = GenericController.class.getDeclaredMethod("getSubControllers");
				method.setAccessible(true);

				List<TestElement> subControllersAndSamplers = (List<TestElement>) method.invoke(controller);
				for (TestElement te : subControllersAndSamplers) {
					if (te instanceof Sampler || te instanceof TransactionController) {
						JMeterProperty drProp = te.getProperty(DependsController.DependsRelationList);
						if (!(drProp instanceof NullProperty)) {
							instance.setExecuteResult((DependsRelationProperty) drProp, null);
						}
					} else if (te instanceof LoopController) {
						resetLoop((LoopController) te);
					}
				}

			} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException
					| InvocationTargetException e1) {
				e1.printStackTrace();
			}
		}
	}
}
